/*
 * Copyright (C) 2011-2013 Eric Lasota
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include "rdx_programmability.hpp"
#include "rdx_objectmanagement.hpp"
#include "rdx_ilcomp.hpp"
#include "rdx_longflow.hpp"
#include "rdx_intrinsics.hpp"
#include "rdx_blockcopy.hpp"

#include "rdx_interpret_internal.hpp"

using namespace RDX::ObjectManagement;
using namespace RDX::Utility;
using namespace RDX::Programmability;
using namespace RDX::Programmability::RuntimeUtilities;
using namespace RDX::ILCompiler;
using namespace RDX::Intrinsics;

#define EXPORT_FRAME(loc)	\
	do\
	{\
		(loc)->bp = reinterpret_cast<RuntimeStackFrame*>(bp);\
		(loc)->ip = nextInstr;\
		(loc)->method = m;\
		(loc)->prv = reinterpret_cast<RuntimeStackValue*>(prv);\
	} while(0)

#define BINARY_ARITHMETIC_OP(opName, type, op)	\
	case opName:\
	{\
		const type *rs = reinterpret_cast<const type*>(bp + instrArgs.ca[0].li);\
		const type *ls = reinterpret_cast<const type*>(bp + instrArgs.ca[0].li + Utility::PaddedSize(sizeof(type), sizeof(RuntimeStackValue)));\
		type *dest = reinterpret_cast<type*>(bp + instrArgs.ca[1].li);\
		*dest = ((*ls) op (*rs));\
	}\
	break

#define BINARY_ARITHMETIC_OP_ZERO_CHECK(opName, type, op)	\
	case opName:\
	{\
		const type *rs = reinterpret_cast<const type*>(bp + instrArgs.ca[0].li);\
		const type *ls = reinterpret_cast<const type*>(bp + instrArgs.ca[0].li + Utility::PaddedSize(sizeof(type), sizeof(RuntimeStackValue)));\
		type *dest = reinterpret_cast<type*>(bp + instrArgs.ca[1].li);\
		if((*rs) == static_cast<type>(0))\
			THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_DivideByZeroException]);\
		*dest = ((*ls) op (*rs));\
	}\
	break

#define UNARY_ARITHMETIC_OP(opName, type, op)	\
	case opName:\
	{\
		const type *v = reinterpret_cast<const type*>(bp + instrArgs.ca[0].li);\
		type *dest = reinterpret_cast<type*>(bp + instrArgs.ca[1].li);\
		*dest = (op (*v));\
	}\
	break


#define CONVERT_OP(opName, srcType, destType)	\
	case opName:\
		ConvertOp<srcType, destType>(bp, instrArgs);\
	break

#define NUMERIC_COMPARE_OP(opName, type, op)	\
	case opName:\
	{\
		const type *rs = reinterpret_cast<const type*>(bp + instrArgs.ca[1].li);\
		const type *ls = reinterpret_cast<const type*>(bp + instrArgs.ca[1].li + Utility::PaddedSize(sizeof(type), sizeof(RuntimeStackValue)));\
		if((*ls) op (*rs))\
			nextInstr = instructions + instrArgs.ca[0].li;\
	}\
	break

#define ITERATE_ARRAY_OP_BASE(opName, elementType, copierType, enableSubs)	\
	case opName:\
		{\
			void *ex = IterateArrayOpBase<elementType, copierType, enableSubs>(objm, bp, instrArgs, instructions, &nextInstr);\
			if(ex != NULL)\
			{\
				THROWEXCEPTION(ex);\
			}\
		}\
		break

		
#define ITERATE_ARRAY_OP(opName, elementType, enableSubs)	\
	ITERATE_ARRAY_OP_BASE(opName, elementType, UnitCopier, enableSubs)


#define MOVE_OP_GROUP(base, copier, type)	\
	MOVE_OP(base, copier, type, 0);\
	MOVE_OP(base, copier, type, 1);\
	MOVE_OP(base, copier, type, 2);\
	MOVE_OP(base, copier, type, 3);\
	MOVE_OP(base, copier, type, 4);\
	MOVE_OP(base, copier, type, 5);\
	MOVE_OP(base, copier, type, 6);\
	MOVE_OP(base, copier, type, 7);\
	MOVE_OP(base, copier, type, 8);\
	MOVE_OP(base, copier, type, 9);\
	MOVE_OP(base, copier, type, 10);\
	MOVE_OP(base, copier, type, 11);\
	MOVE_OP(base, copier, type, 12);\
	MOVE_OP(base, copier, type, 13);\
	MOVE_OP(base, copier, type, 14);\
	MOVE_OP(base, copier, type, 15);\
	MOVE_OP(base, copier, type, 16);\
	MOVE_OP(base, copier, type, 17);\
	MOVE_OP(base, copier, type, 18);\
	MOVE_OP(base, copier, type, 19);\
	MOVE_OP(base, copier, type, 20);\
	MOVE_OP(base, copier, type, 21);\
	MOVE_OP(base, copier, type, 22);\
	MOVE_OP(base, copier, type, 23)

#define MOVE_SRC_MODE_REGULAR	0
#define MOVE_SRC_MODE_PARENT	1
#define MOVE_SRC_MODE_ABSOLUTE	2
#define MOVE_SRC_MODE_COUNT		3

#define MOVE_SRC_DEREF_REGULAR	0
#define MOVE_SRC_DEREF_DEREF	1
#define MOVE_SRC_DEREF_COUNT	2

#define MOVE_DEST_MODE_REGULAR	0
#define MOVE_DEST_MODE_PARENT	1
#define MOVE_DEST_MODE_COUNT	2

#define MOVE_DEST_DEREF_REGULAR	0
#define MOVE_DEST_DEREF_DEREF	1
#define MOVE_DEST_DEREF_COUNT	2

#define MOVE_OP(base, copier, type, index)	\
	case (base + index):\
	{\
		MoveOp<type, index, copier>(bp, prv, instrArgs);\
	}\
	break


#define BRANCH_OP(opcode, moveType, pointer, ifEqual)	\
	case opcode:\
	{\
		EqJumpOp<moveType, pointer, ifEqual>(bp, instructions, nextInstr, instrArgs);\
	}\
	break


namespace RDX
{
	namespace Interpreter
	{
		using namespace ILOpcodes;

		struct SingleArgIntrinsic
		{
			ILOpcode originalOp;
			ILOpcode newOp;
			LargeInt p2check;
		};

		struct DualArgIntrinsic
		{
			ILOpcode originalOp;
			ILOpcode newOp;
			LargeInt p2check;
			LargeInt p3check;
		};

		static const SingleArgIntrinsic SINGLE_ARG_INTRINSICS[] =
		{
			{ ILOP_iadd,	ILOP_iadd4,		4 },
			{ ILOP_isub,	ILOP_isub4,		4 },
			{ ILOP_imul,	ILOP_imul4,		4 },
			{ ILOP_idiv,	ILOP_idiv4,		4 },
			{ ILOP_imod,	ILOP_imod4,		4 },
			{ ILOP_ineg,	ILOP_ineg4,		4 },
			
			{ ILOP_ilt,		ILOP_ilt4,		4 },
			{ ILOP_igt,		ILOP_igt4,		4 },
			{ ILOP_ile,		ILOP_ile4,		4 },
			{ ILOP_ige,		ILOP_ige4,		4 },
			{ ILOP_ieq,		ILOP_ieq4,		4 },
			{ ILOP_ine,		ILOP_ine4,		4 },

			{ ILOP_iadd,	ILOP_iadd8,		8 },
			{ ILOP_isub,	ILOP_isub8,		8 },
			{ ILOP_imul,	ILOP_imul8,		8 },
			{ ILOP_idiv,	ILOP_idiv8,		8 },
			{ ILOP_imod,	ILOP_imod8,		8 },
			{ ILOP_ineg,	ILOP_ineg8,		8 },

			{ ILOP_ilt,		ILOP_ilt8,		8 },
			{ ILOP_igt,		ILOP_igt8,		8 },
			{ ILOP_ile,		ILOP_ile8,		8 },
			{ ILOP_ige,		ILOP_ige8,		8 },
			{ ILOP_ieq,		ILOP_ieq8,		8 },
			{ ILOP_ine,		ILOP_ine8,		8 },

			{ ILOP_fadd,	ILOP_fadd4,		4 },
			{ ILOP_fsub,	ILOP_fsub4,		4 },
			{ ILOP_fmul,	ILOP_fmul4,		4 },
			{ ILOP_fdiv,	ILOP_fdiv4,		4 },
			{ ILOP_fneg,	ILOP_fneg4,		4 },
			
			{ ILOP_flt4,	ILOP_flt4,		4 },
			{ ILOP_fgt4,	ILOP_fgt4,		4 },
			{ ILOP_fle4,	ILOP_fle4,		4 },
			{ ILOP_fge4,	ILOP_fge4,		4 },
			{ ILOP_feq4,	ILOP_feq4,		4 },
			{ ILOP_fne4,	ILOP_fne4,		4 },
		};

		static const DualArgIntrinsic DUAL_ARG_INTRINSICS[] =
		{
			{ ILOP_isx,		ILOP_isx18,		1,	8 },
			{ ILOP_isx,		ILOP_isx28,		2,	8 },
			{ ILOP_isx,		ILOP_isx48,		4,	8 },
			{ ILOP_isx,		ILOP_isx14,		1,	4 },
			{ ILOP_isx,		ILOP_isx24,		2,	4 },
			{ ILOP_isx,		ILOP_isx12,		1,	2 },
			
			{ ILOP_isx,		ILOP_ict84,		8,	4 },
			{ ILOP_isx,		ILOP_ict82,		8,	2 },
			{ ILOP_isx,		ILOP_ict81,		8,	1 },
			{ ILOP_isx,		ILOP_ict42,		4,	2 },
			{ ILOP_isx,		ILOP_ict41,		4,	1 },
			{ ILOP_isx,		ILOP_ict21,		2,	1 },
			
			{ ILOP_isx,		ILOP_nconv8,		8,	8 },
			{ ILOP_isx,		ILOP_nconv4,		4,	4 },
			{ ILOP_isx,		ILOP_nconv2,		2,	2 },
			{ ILOP_isx,		ILOP_nconv1,		1,	1 },
			
			{ ILOP_itof,	ILOP_itof44,	4,	4 },
			{ ILOP_itof,	ILOP_itof48,	4,	8 },
			{ ILOP_itof,	ILOP_itof84,	8,	4 },
			{ ILOP_itof,	ILOP_itof88,	8,	8 },
			
			{ ILOP_ftoi,	ILOP_ftoi44,	4,	4 },
			{ ILOP_ftoi,	ILOP_ftoi48,	4,	8 },
			{ ILOP_ftoi,	ILOP_ftoi84,	8,	4 },
			{ ILOP_ftoi,	ILOP_ftoi88,	8,	8 },
		};

		
		template<class srcType, class destType>
		inline void ConvertOp(UInt8 *bp, const ILInstruction::ArgPtrUnion &instrArgs)
		{
			const srcType *src = reinterpret_cast<const srcType *>(bp + instrArgs.ca[0].li);
			destType *dest = reinterpret_cast<destType *>(bp + instrArgs.ca[1].li);
			*dest = static_cast<destType>(*src);
		}

		struct UnitCopier
		{
			template<class elementType, int sizeArgNum>
			inline static void Copy(elementType *destPtr, const elementType *arrayRef, LargeInt index, const ILInstruction::ArgPtrUnion &instrArgs)
			{
				*destPtr = arrayRef[index];
			}
		};

		struct BlockCopier
		{
			template<class elementType, int sizeArgNum>
			inline static void Copy(elementType *destPtr, const elementType *arrayRef, LargeInt index, const ILInstruction::ArgPtrUnion &instrArgs)
			{
				BlockCopy(destPtr, reinterpret_cast<const UInt8 *>(arrayRef) + instrArgs.ca[sizeArgNum].li * index, static_cast<size_t>(instrArgs.ca[sizeArgNum].li));
			}
		};

		// Returns NULL for no exception, or may return an exception
		template<class elementType, class copierType, bool enableSubs>
		inline void *IterateArrayOpBase(IObjectManager *objm, UInt8 *bp, const ILInstruction::ArgPtrUnion &instrArgs, const ILInstruction *instructions, const ILInstruction **nextInstr)
		{
			const elementType *arrayRef = *reinterpret_cast<const elementType **>(bp + instrArgs.ca[0].li);
			LargeInt *indexPtr = reinterpret_cast<LargeInt *>(bp + instrArgs.ca[1].li);
			elementType *destPtr = reinterpret_cast<elementType *>(bp + instrArgs.ca[2].li);
			if(arrayRef == NULL)
				return objm->GetBuiltIns()->providerDictionary[X_NullReferenceException];
			LargeInt numElements = GCInfo::From(arrayRef)->numElements;
			LargeInt nextIndex = (*indexPtr) + 1;
			if(nextIndex < 0 || nextIndex >= numElements)
			{
				*nextInstr = instructions + instrArgs.ca[3].li;
				return NULL;
			}

			LargeInt numDimensions;

			if(enableSubs)
			{
				numDimensions = instrArgs.ca[7].li;
				if(numDimensions != GCInfo::From(arrayRef)->numDimensions)
					return objm->GetBuiltIns()->providerDictionary[X_IndexOutOfBoundsException];
			}

			// Nothing can fail now, commit changes
			// Save next index
			*indexPtr = nextIndex;
			// Extract value
			copierType::Copy<elementType, 4>(destPtr, arrayRef, nextIndex, instrArgs);

			if(enableSubs)
			{
				LargeInt *subIndexLocals = reinterpret_cast<LargeInt *>(bp + instrArgs.ca[6].li);
				const LargeInt *dimensions = GCInfo::From(arrayRef)->dimensions;
				for(LargeInt dimensionIndex=numDimensions-1; ;dimensionIndex--)
				{
					LargeInt *subIndexLocal = subIndexLocals - dimensionIndex;	/* Locals allocate down */
					LargeInt incremented = (*subIndexLocal) + 1;
					if(incremented == dimensions[dimensionIndex] && dimensionIndex != 0)
						*subIndexLocal = 0;	/* Carry */
					else
					{
						*subIndexLocal = incremented;
						break;
					}
				}
			}

			return NULL;
		}

		template<class moveT, bool pointer, bool ifEqual>
		inline void EqJumpOp(UInt8 *bp, const ILInstruction *instructions, const ILInstruction *&nextInstr, const ILInstruction::ArgPtrUnion &instrArgs)
		{
			const void *src1 = reinterpret_cast<const void*>(bp + instrArgs.ca[1].li);
			const void *src2 = reinterpret_cast<const void*>(bp + instrArgs.ca[2].li);

			if(pointer)
			{
				src1 = static_cast<const RuntimePointer<void> *>(src1)->valueRef;
				src2 = static_cast<const RuntimePointer<void> *>(src2)->valueRef;
			}

			if((*static_cast<const moveT *>(src1) == *static_cast<const moveT *>(src2)) == ifEqual)
				nextInstr = instructions + instrArgs.ca[0].li;
		}

		template<class moveT, int index, class copierT>
		inline void MoveOp(UInt8 *bp, UInt8 *prv, const ILInstruction::ArgPtrUnion &instrArgs)
		{
			int srcMode = index % MOVE_SRC_MODE_COUNT;
			int srcDeref = (index / MOVE_SRC_MODE_COUNT) % MOVE_SRC_DEREF_COUNT;
			int destMode = (index / (MOVE_SRC_MODE_COUNT*MOVE_SRC_DEREF_COUNT)) % MOVE_DEST_MODE_COUNT;
			int destDeref = (index / (MOVE_SRC_MODE_COUNT*MOVE_SRC_DEREF_COUNT*MOVE_DEST_MODE_COUNT)) % MOVE_DEST_DEREF_COUNT;
			const void *src;
			void *dest;
			if(srcMode == MOVE_SRC_MODE_PARENT)
				src = prv + instrArgs.ca[0].li;
			else if(srcMode == MOVE_SRC_MODE_ABSOLUTE)
				src = instrArgs.ca[0].p;
			else
				src = bp + instrArgs.ca[0].li;

			if(srcDeref)
				src = reinterpret_cast<const RuntimePointer<void> *>(src)->valueRef;
			if(destMode == MOVE_DEST_MODE_PARENT)
				dest = prv + instrArgs.ca[1].li;
			else
				dest = bp + instrArgs.ca[1].li;
			if(destDeref)
				dest = reinterpret_cast<RuntimePointer<void> *>(dest)->valueRef;

			copierT::Copy<moveT, 2>(static_cast<moveT*>(dest), static_cast<const moveT*>(src), 0, instrArgs);
		}

		inline static bool IsBigEndian()
		{
			union
			{
				Int16 s;
				UInt8 bytes[2];
			} u;
			u.s = 1;
			return u.bytes[0] == 0;
		}

		int ThrowException(RuntimeThread *t, Exception *e, const Method *m, const void *ip, RuntimeStackFrame *bp, RuntimeStackValue *prv)
		{
			t->ex = e;
			t->frame.ip = ip;
			t->frame.bp = bp;
			t->frame.method = m;
			t->frame.prv = prv;
			return RuntimeState::Exception;
		}

		#define THROWEXCEPTION(e) return ThrowException(thread, static_cast<Exception*>(e), m, nextInstr, reinterpret_cast<RuntimeStackFrame*>(bp), reinterpret_cast<RuntimeStackValue*>(prv))

		int ResumeThreadInterpreted(OperationContext *ctx, IObjectManager *objm, RuntimeThread *thread)
		{
			UInt8 *bp = reinterpret_cast<UInt8*>(thread->frame.bp);
			UInt8 *prv = reinterpret_cast<UInt8*>(thread->frame.prv);
			const ILInstruction *nextInstr = reinterpret_cast<const ILInstruction *>(thread->frame.ip);
			const Method *m = thread->frame.method;
			const ILInstruction *instructions = static_cast<const ILInstruction *>(m->_native.nativeInstructions);
			UInt8 *stackBase = thread->stackBytes;

			const GCInfo *mInfo = GCInfo::From(m);

			LargeInt numInstrs = GCInfo::From(instructions)->numElements;

			while(true)
			{

				const ILInstruction *instr = nextInstr++;
				const ILInstruction::ArgPtrUnion &instrArgs = instr->arg;
				LargeInt instrNum = instr - instructions;

				switch(instr->opcode)
				{
				case ILOP_debuginfo:
				case ILOP_hardenstack:
					break;

				// <src> <dest> <size> <alignment> <loc flags>    Copy current frame to current frame
				case ILOP_move:
					{
						const void *src;
						void *dest;
						if(instrArgs.ca[4].li & ILOpcodes::ILOP_MOVE_SRC_PARENT_FRAME)
							src = prv + instrArgs.ca[0].li;
						else if(instrArgs.ca[4].li & ILOpcodes::ILOP_MOVE_SRC_TYPE_DEFAULT)
						{
							const StructuredType *st = static_cast<const StructuredType *>(instrArgs.ca[0].p);
							src = st->_native.actualDefaultValue;
						}
						else
							src = bp + instrArgs.ca[0].li;
						if(instrArgs.ca[4].li & ILOpcodes::ILOP_MOVE_SRC_DEREF)
							src = reinterpret_cast<const RuntimePointer<void> *>(src)->valueRef;

						if(instrArgs.ca[4].li & ILOpcodes::ILOP_MOVE_DEST_PARENT_FRAME)
							dest = prv + instrArgs.ca[1].li;
						else
							dest = bp + instrArgs.ca[1].li;
						if(instrArgs.ca[4].li & ILOpcodes::ILOP_MOVE_DEST_DEREF)
							dest = reinterpret_cast<RuntimePointer<void> *>(dest)->valueRef;

						const LargeInt *srcint = static_cast<const LargeInt*>(src);
						LargeInt *destint = static_cast<LargeInt*>(dest);
						void *const*srcp = static_cast<void*const*>(src);
						void **destp = static_cast<void**>(dest);

						BlockCopy(dest, src, instrArgs.ca[2].lui);
					}
					break;
				// <dest> <type> <src>             Converts a runtime pointer at the destination to a typed runtime pointer
				case ILOP_tovarying:
					{
						RuntimePointer<void> *rsp = reinterpret_cast<RuntimePointer<void>*>(bp + instrArgs.ca[2].li);
						TypedRuntimePointer *trp = reinterpret_cast<TypedRuntimePointer*>(bp + instrArgs.ca[0].li);
						trp->rtp = *rsp;
						trp->type = static_cast<const Type *>(instrArgs.ca[1].p);
					}
					break;
				// <loc> <type>                    Converts a runtime pointer at the destination to a typed runtime pointer
				case ILOP_tovarying_static:
					{
						TypedRuntimePointer *trp = reinterpret_cast<TypedRuntimePointer*>(bp + instrArgs.ca[0].li);
						trp->type = static_cast<const Type *>(instrArgs.ca[1].p);
					}
					break;
				// <dest> <offset>                 Creates a pointer to the current frame + offset
				case ILOP_pinl:
					{
						RuntimePointer<void> *rsp = reinterpret_cast<RuntimePointer<void> *>(bp + instrArgs.ca[0].li);
						rsp->objectRef = thread;
						rsp->valueRef = bp + instrArgs.ca[1].li;
					}
					break;
				// <dest> <offset>                 Creates a pointer to the parent frame + offset
				case ILOP_pinp:
					{
						RuntimePointer<void> *rsp = reinterpret_cast<RuntimePointer<void> *>(bp + instrArgs.ca[0].li);
						rsp->objectRef = thread;
						rsp->valueRef = prv + instrArgs.ca[1].li;
					}
					break;
				// <loc> <offset>               Increases a pointer's size by offset
				case ILOP_incptr:
					{
						RuntimePointer<void> *rsp = reinterpret_cast<RuntimePointer<void> *>(bp + instrArgs.ca[0].li);
						char *p = reinterpret_cast<char*>(rsp->valueRef);
						rsp->valueRef = p + instrArgs.ca[1].li;
					}
					break;
				// <src> <dest> <offset>                 NULL check src and create a pointer to the start of src + offset
				case ILOP_objinterior:
					{
						void *p = *reinterpret_cast<void**>(bp + instrArgs.ca[0].li);
						if(p == NULL)
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);

						RuntimePointer<void> *rsp = reinterpret_cast<RuntimePointer<void> *>(bp + instrArgs.ca[1].li);
						rsp->objectRef = p;
						rsp->valueRef = reinterpret_cast<char*>(p) + instrArgs.ca[2].li;
					}
					break;
				// <src> <dest> <offset>                 create a pointer to the start of src + offset
				case ILOP_objinterior_notnull:
				case ILOP_objinterior_notnull_persist:
					{
						void *p = *reinterpret_cast<void**>(bp + instrArgs.ca[0].li);
						RuntimePointer<void> *rsp = reinterpret_cast<RuntimePointer<void> *>(bp + instrArgs.ca[1].li);
						rsp->objectRef = p;
						rsp->valueRef = reinterpret_cast<char*>(p) + instrArgs.ca[2].li;
					}
					break;
				// <LA0:value> <LA1[0]:dest> <LA1[1]:size>        Copy instruction value to current frame
				case ILOP_immediate:
					{
						void *p = bp + instrArgs.la[1].ca[0].li;
						BlockCopy(p, &instrArgs.la[0], instrArgs.la[1].ca[1].lui);
					}
					break;
				case ILOP_immediate1:
					{
						UInt8 *p = reinterpret_cast<UInt8*>(bp + instrArgs.la[1].ca[0].li);
						*p = instrArgs.la[0].ca[0].u8;
					}
					break;
				case ILOP_immediate2:
					{
						UInt16 *p = reinterpret_cast<UInt16*>(bp + instrArgs.la[1].ca[0].li);
						*p = instrArgs.la[0].ca[0].u16;
					}
					break;
				case ILOP_immediate4:
					{
						UInt32 *p = reinterpret_cast<UInt32*>(bp + instrArgs.la[1].ca[0].li);
						*p = instrArgs.la[0].ca[0].u32;
					}
					break;
				case ILOP_immediate8:
					{
						UInt64 *p = reinterpret_cast<UInt64*>(bp + instrArgs.la[1].ca[0].li);
						*p = instrArgs.la[0].u64;
					}
					break;
				case ILOP_immediate_ptr:
					{
						void *p = bp + instrArgs.la[1].ca[0].li;
						*static_cast<const void**>(p) = instrArgs.la[0].ca[0].p;
					}
					break;
				case ILOP_immediate_rtp:
					{
						void *p = bp + instrArgs.la[1].ca[0].li;
						*static_cast<RuntimePointer<void> *>(p) = instrArgs.la[0].rtp;
					}
					break;
				// <indexsrc> <arraysrc> <dest> <offset>          NULL check arraysrc from current frame, push internal reference to array head + offset
				case ILOP_arrayindex:
					{
						void *o = *reinterpret_cast<void**>(bp + instrArgs.ca[1].li);

						if(o == NULL)
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);

						void *interior = ArrayIndex(o, reinterpret_cast<const RuntimeStackValue*>(bp + instrArgs.ca[0].li));
						if(interior == NULL)
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_IndexOutOfBoundsException]);

						RuntimePointer<void> *rsp = reinterpret_cast<RuntimePointer<void> *>(bp + instrArgs.ca[2].li);
						rsp->objectRef = o;
						rsp->valueRef = reinterpret_cast<char*>(interior) + instrArgs.ca[3].li;
					}
					break;
				// <paramsrc> <rvdest> <objsrc> <(I)vstidx> <interface> Loads the target method, null checks, and transfers control
				case ILOP_callinterface:
				// <paramsrc> <rvdest> <objsrc> <(Int)vstidx>     Loads the target method, null checks, and transfers control
				case ILOP_callvirtual:
				// <paramsrc> <rvdest> <method>                   Transfers control to the target method
				case ILOP_calldelegatebp:
				case ILOP_calldelegateprv:
				// <paramsrc> <rvdest> <methodsrc>                Null checks and transfers control to the target method
				case ILOP_call:
					{
						const Method *invokedMethod;
						switch(instr->opcode)
						{
						case ILOP_callvirtual:
							{
								void *o = *reinterpret_cast<void**>(bp + instrArgs.ca[2].li);
								const GCInfo *objInfo = GCInfo::From(o);
								const GCInfo *objTypeInfo = GCInfo::From(objInfo->containerType);
								const StructuredType *st = static_cast<const StructuredType *>(objInfo->containerType);

								if(o == NULL)
									THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);

								invokedMethod = GCInfo::From(o)->vft[instrArgs.ca[3].li];
							}
							break;
						case ILOP_callinterface:
							{
								void *o = *reinterpret_cast<void**>(bp + instrArgs.ca[2].li);

								if(o == NULL)
									THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);

								const Type *interfaceType = static_cast<const Type*>(instrArgs.ca[4].p);

								LargeInt vftOffset = 0;
								LargeInt numInterfaces = 0;
								const InterfaceImplementation *interfaceImpls = static_cast<const StructuredType*>(GCInfo::From(o)->containerType)->interfaces;
								numInterfaces = GCInfo::From(interfaceImpls)->numElements;
								for(LargeInt i=0;i<numInterfaces;i++)
								{
									const InterfaceImplementation *impl = interfaceImpls + i;
									if(impl->type == interfaceType)
									{
										vftOffset = impl->vftOffset;
										break;
									}
								}

								invokedMethod = GCInfo::From(o)->vft[instrArgs.ca[3].li + vftOffset];
							}
							break;
						case ILOP_call:
							invokedMethod = static_cast<const Method*>(instrArgs.ca[2].p);
							break;
						case ILOP_calldelegatebp:
							invokedMethod = *(reinterpret_cast<const Method**>(bp + instrArgs.ca[2].li));
							break;
						case ILOP_calldelegateprv:
							invokedMethod = *(reinterpret_cast<const Method**>(prv + instrArgs.ca[2].li));
							break;
						default:
							invokedMethod = NULL;	// To shut up compiler warning
							break;
						}

						if(!invokedMethod)
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);
						
						RuntimeStackFrame currentFrame;
						currentFrame.bp = reinterpret_cast<RuntimeStackFrame*>(bp);
						currentFrame.ip = nextInstr;
						currentFrame.method = m;
						currentFrame.prv = reinterpret_cast<RuntimeStackValue*>(prv);

						const GCInfo *invokedMethodInfo = GCInfo::From(invokedMethod);

						NativeCallback cb = invokedMethod->_native.nativeCall;

						if(!invokedMethod->_native.isNativeCall)
						{
							if(invokedMethod->bytecode == NULL)
								THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);

							// Re-enter
							RuntimeStackFrame newFrame;
							if(!EnterMethodInline(objm, invokedMethod, &currentFrame, bp + instrArgs.ca[0].li, stackBase,
								reinterpret_cast<RuntimeStackValue*>(bp + instrArgs.ca[1].li), &newFrame))
								THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_StackOverflowException]);

							if(newFrame.method->_native.precompiledCodeModule)
							{
								thread->frame = newFrame;
								return RuntimeState::Active;
							}
							
							bp = reinterpret_cast<UInt8*>(newFrame.bp);
							prv = reinterpret_cast<UInt8*>(newFrame.prv);
							nextInstr = reinterpret_cast<const ILInstruction *>(newFrame.ip);
							m = newFrame.method;
							instructions = static_cast<const ILInstruction *>(m->_native.nativeInstructions);
						}
						else
						{
							if(cb == NULL)
								THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);

							thread->frame = currentFrame;
							thread->insertionPoint = bp + instrArgs.ca[0].li;
							thread->activeNativeMethod = invokedMethod;

							int status = cb(ctx, objm, invokedMethod, thread, reinterpret_cast<RuntimeStackValue*>(bp + instrArgs.ca[1].li));
							thread->activeNativeMethod = NULL;

							if(status <= 0)
							{
								thread->frame = currentFrame;
								return status;
							}
						}
					}
					break;
				// <loc> <instr offset>                                 Verify that a current-frame value is not null
				case ILOP_verifynotnull:
					{
						void *o = *reinterpret_cast<void**>(bp + instrArgs.ca[0].li);
						if(o == NULL)
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);
					}
					break;
				case ILOP_zero:
					{
						memset(bp + instrArgs.ca[0].li, 0, instrArgs.ca[1].lui);
					}
					break;
				case ILOP_zero1:
					{
						*reinterpret_cast<UInt8*>(bp + instrArgs.ca[0].li) = 0;
					}
					break;
				case ILOP_zero2:
					{
						*reinterpret_cast<UInt16*>(bp + instrArgs.ca[0].li) = 0;
					}
					break;
				case ILOP_zero4:
					{
						*reinterpret_cast<UInt32*>(bp + instrArgs.ca[0].li) = 0;
					}
					break;
				case ILOP_zero8:
					{
						*reinterpret_cast<UInt64*>(bp + instrArgs.ca[0].li) = 0;
					}
					break;
				// <loc> <type> <dimension src> <num dimensions>  Creates a new instance of the specified type in the current frame
				case ILOP_newinstance:
					{
						void *o;

						// Allocations can block, make sure the IP is up to date so the thread can be traced
						EXPORT_FRAME(&thread->frame);

						o = NewObjectInstance(ctx, objm, static_cast<const Type*>(instrArgs.ca[1].p),
							reinterpret_cast<RuntimeStackValue*>(bp + instrArgs.ca[2].li),
							instrArgs.ca[3].li);
						if(!o)
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_AllocationFailureException]);
						*reinterpret_cast<void**>(bp + instrArgs.ca[0].li) = o;
					}
					break;
				case ILOP_exit:
					{
						RuntimeStackFrame *rfp = reinterpret_cast<RuntimeStackFrame *>(bp);
						if(!rfp->method || rfp->method->_native.precompiledCodeModule)
						{
							thread->frame.bp = rfp;
							return RuntimeState::AbandonFrame;
						}

						nextInstr = static_cast<const ILInstruction*>(rfp->ip);
						prv = reinterpret_cast<UInt8*>(rfp->prv);
						m = rfp->method;
						instructions = static_cast<const ILInstruction *>(m->_native.nativeInstructions);
						bp = reinterpret_cast<UInt8*>(rfp->bp);
					}
					break;
				// <instrnum>                                     Jumps to the specified instruction
				case ILOP_jump:
					{
						nextInstr = instructions + instrArgs.ca[0].li;
					}
					break;
				// <instrnum> <src>                               Jumps to the specified instruction if the target value is true
				case ILOP_jtrue:
					{
						if(*reinterpret_cast<Bool*>(bp + instrArgs.ca[1].li) != FalseValue)
							nextInstr = instructions + instrArgs.ca[0].li;
					}
					break;
				// <instrnum> <src>                               Jumps to the specified instruction if the target value is not true
				case ILOP_jfalse:
					{
						if(*reinterpret_cast<Bool*>(bp + instrArgs.ca[1].li) == FalseValue)
							nextInstr = instructions + instrArgs.ca[0].li;
					}
					break;
				// <instrnum> <src> <type>                        Jumps to the specified instruction if the target value inherits from the specified type
				case ILOP_jinherits:
					{
						void *o = *reinterpret_cast<void**>(bp + instrArgs.ca[1].li);
						const Type *t = reinterpret_cast<const Type*>(instrArgs.ca[2].p);

						if(o == NULL)
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);

						if(objm->ObjectCompatible(o, t))
							nextInstr = instructions + instrArgs.ca[0].li;
					}
					break;
				case ILOP_jccp:
					{
						Type const*const* rs = reinterpret_cast<Type const*const*>(bp + instrArgs.ca[1].li);
						void const*const* ls = reinterpret_cast<void const*const*>(bp + instrArgs.ca[1].li + Utility::PaddedSize(sizeof(void *), sizeof(RuntimeStackValue)));
						if(*ls == NULL)
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);

						if(objm->ObjectCompatible(*ls, *rs))
							nextInstr = instructions + instrArgs.ca[0].li;
					}
					break;
				case ILOP_jccf:
					{
						Type const*const* rs = reinterpret_cast<Type const*const*>(bp + instrArgs.ca[1].li);
						void const*const* ls = reinterpret_cast<void const*const*>(bp + instrArgs.ca[1].li + Utility::PaddedSize(sizeof(void *), sizeof(RuntimeStackValue)));
						if(*ls == NULL)
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);

						if(!objm->ObjectCompatible(*ls, *rs))
							nextInstr = instructions + instrArgs.ca[0].li;
					}
					break;
				case ILOP_tick:
					{
						if(Threading::NonAtomicDecrement(&thread->timeout) == 0)
						{
							thread->frame.bp = reinterpret_cast<RuntimeStackFrame*>(bp);
							thread->frame.prv = reinterpret_cast<RuntimeStackValue*>(prv);
							thread->frame.ip = nextInstr;
							thread->frame.method = m;
							Threading::Synchronize();
							return RuntimeState::TimedOut;
						}
					}
					break;

				// <src> <type>                                   Throws IncompatibleConversionException if value in current frame can not be found in a specified enum table.
				case ILOP_assertenum:
					{
						const EnumValue ev = *reinterpret_cast<const EnumValue *>(bp + instrArgs.ca[0].li);
						const Enumerant *enums = static_cast<const StructuredType *>(instrArgs.ca[1].p)->enumerants;

						if(!objm->EnumCompatible(ev, enums))
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_IncompatibleConversionException]);
					}
					break;

				// <src> <type>                                   Throws IncompatibleConversionException if value in current frame can not be cast to type, value may be null
				case ILOP_assertinherits:
					{
						const void *o = *reinterpret_cast<void**>(bp + instrArgs.ca[0].li);
						const Type *t = reinterpret_cast<const Type*>(instrArgs.ca[1].p);

						if(!objm->ObjectCompatible(o, t))
							THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_IncompatibleConversionException]);
					}
					break;

				// <src> <dest> <size> <align>                    Generates a hashcode from the current frame and stores it at dest
				case ILOP_hash_f:
					{
						const void *data = reinterpret_cast<const void *>(bp + instrArgs.ca[0].li);
						HashValue *dest = reinterpret_cast<HashValue*>(bp + instrArgs.ca[1].li);
						*dest = Utility::HashBytes(data, static_cast<LargeUInt>(instrArgs.ca[2].li));
					}
					break;
				// <src> <dest> <size> <align>                    Generates a hashcode from a pointer on current frame and stores it at dest
				case ILOP_hash_p:
					{
						const RuntimePointer<void> *dataPtr = reinterpret_cast<const RuntimePointer<void> *>(bp + instrArgs.ca[0].li);
						HashValue *dest = reinterpret_cast<HashValue*>(bp + instrArgs.ca[1].li);
						*dest = Utility::HashBytes(dataPtr->valueRef, static_cast<LargeUInt>(instrArgs.ca[2].li));
					}
					break;
				// <instrnum> <src1> <src2> <size> <align>        Jumps to the specified instruction if the two specified values are equal
				case ILOP_jeq_f:
					{
						const void *src1 = reinterpret_cast<const void*>(bp + instrArgs.ca[1].li);
						const void *src2 = reinterpret_cast<const void*>(bp + instrArgs.ca[2].li);
						if(!memcmp(src1, src2, instrArgs.ca[3].lui))
							nextInstr = instructions + instrArgs.ca[0].li;
					}
					break;
				// <instrnum> <src1> <src2> <size> <align>        Jumps to the specified instruction if the two specified pointers are equal
				case ILOP_jeq_p:
					{
						const RuntimePointer<void> *ptr1 = reinterpret_cast<const RuntimePointer<void>*>(bp + instrArgs.ca[1].li);
						const RuntimePointer<void> *ptr2 = reinterpret_cast<const RuntimePointer<void>*>(bp + instrArgs.ca[2].li);
						if(!memcmp(ptr1->valueRef, ptr2->valueRef, instrArgs.ca[3].lui))
							nextInstr = instructions + instrArgs.ca[0].li;
					}
					break;
				// <instrnum> <src1> <src2> <size> <align>        Jumps to the specified instruction if the two specified values are not equal
				case ILOP_jne_f:
					{
						const void *src1 = reinterpret_cast<const void*>(bp + instrArgs.ca[1].li);
						const void *src2 = reinterpret_cast<const void*>(bp + instrArgs.ca[2].li);
						if(memcmp(src1, src2, instrArgs.ca[3].lui))
							nextInstr = instructions + instrArgs.ca[0].li;
					}
					break;
				// <instrnum> <src1> <src2> <size> <align>        Jumps to the specified instruction if the two specified pointers are not equal
				case ILOP_jne_p:
					{
						const RuntimePointer<void> *ptr1 = reinterpret_cast<const RuntimePointer<void>*>(bp + instrArgs.ca[1].li);
						const RuntimePointer<void> *ptr2 = reinterpret_cast<const RuntimePointer<void>*>(bp + instrArgs.ca[2].li);
						if(memcmp(ptr1->valueRef, ptr2->valueRef, instrArgs.ca[3].lui))
							nextInstr = instructions + instrArgs.ca[0].li;
					}
					break;
				// <src>                                          Throws the exception at src.  If src is null, throws UnspecifiedException
				case ILOP_throw:
					{
						Exception *e = *reinterpret_cast<Exception**>(bp + instrArgs.ca[0].li);
						if(!e)
							e = static_cast<Exception*>(objm->GetBuiltIns()->providerDictionary[X_UnspecifiedException]);
						THROWEXCEPTION(e);
					}
					break;
				case ILOP_xnullref:
					{
						THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_NullReferenceException]);
					}
					break;
				case ILOP_catch:
					{
						THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_InvalidOperationException]);
					}
					break;
				case ILOP_fatal:
					{
						THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_InvalidOperationException]);
					}
					break;

				// Converts a signed integer of size p2 to a signed integer of size p3
				case ILOP_isx:
					{
						if(instrArgs.ca[3].li <= instrArgs.ca[2].li)
						{
							BlockCopy(bp + instrArgs.ca[1].li, bp + instrArgs.ca[0].li, instrArgs.ca[3].lui);
						}
						else
						{
							if(IsBigEndian())
							{
								union
								{
									UInt8 bytes[2];
									LargeInt extended;
								} u;

								const UInt8 *originalBytes = bp + instrArgs.ca[0].li;
								LargeInt origSize = instrArgs.ca[2].li;

								Int8 extendByte = static_cast<Int8>(originalBytes[0]);
								u.extended = extendByte;
								UInt8 extensionByte = u.bytes[0];

								UInt8 *outBytes = static_cast<UInt8 *>(bp + instrArgs.ca[1].li);
								LargeInt extSize = instrArgs.ca[3].li - instrArgs.ca[2].li;
								while(extSize--)
									*outBytes++ = extensionByte;
								while(origSize--)
									*outBytes++ = *originalBytes++;
							}
							else
							{
								// Little endian
								union
								{
									UInt8 bytes[2];
									LargeInt extended;
								} u;

								const UInt8 *originalBytes = bp + instrArgs.ca[0].li;
								LargeInt origSize = instrArgs.ca[2].li;

								Int8 extendByte = static_cast<Int8>(originalBytes[origSize-1]);
								u.extended = extendByte;
								UInt8 extensionByte = u.bytes[1];

								UInt8 *outBytes = static_cast<UInt8 *>(bp + instrArgs.ca[1].li);
								LargeInt extSize = instrArgs.ca[3].li - instrArgs.ca[2].li;
								while(origSize--)
									*outBytes++ = *originalBytes++;
								while(extSize--)
									*outBytes++ = extensionByte;
							}
						}
					}
					break;

				// <src> <array res> <count> <size> <align>
				case ILOP_switch:
				case ILOP_switch_ptr:
					{
						const UInt8 *valSrc = bp + instrArgs.ca[0].li;
						if(instr->opcode == ILOP_switch_ptr)
							valSrc = static_cast<const UInt8 *>(reinterpret_cast<const RuntimePointer<void> *>(valSrc)->valueRef);

						const UInt8 *arr = reinterpret_cast<const UInt8 *>(instrArgs.ca[1].p);
						LargeInt numCases = instrArgs.ca[2].li;
						LargeInt size = instrArgs.ca[3].li;

						LargeInt selectedCase;
						if(size == sizeof(EnumValue))
						{
							// Most switches will probably be sizeof(EnumValue), so fast-track this
							EnumValue valE = *reinterpret_cast<const EnumValue *>(valSrc);
							const EnumValue *arrE = reinterpret_cast<const EnumValue *>(arr);
							for(selectedCase=0;selectedCase<numCases;selectedCase++)
							{
								if(arrE[selectedCase] == valE)
									break;	// Matched
							}
						}
						else
						{
							for(selectedCase=0;selectedCase<numCases;selectedCase++)
							{
								if(!memcmp(arr + size*selectedCase, valSrc, static_cast<size_t>(size)))
									break;	// Matched
							}
						}
						
						nextInstr += selectedCase;
					}
					break;

				ITERATE_ARRAY_OP_BASE(ILOP_iteratearray, void, BlockCopier, false);
				ITERATE_ARRAY_OP(ILOP_iteratearray4, Int32, false);
				ITERATE_ARRAY_OP(ILOP_iteratearray8, Int64, false);

				ITERATE_ARRAY_OP_BASE(ILOP_iteratearraysub, void, BlockCopier, true);
				ITERATE_ARRAY_OP(ILOP_iteratearraysub4, Int32, true);
				ITERATE_ARRAY_OP(ILOP_iteratearraysub8, Int64, true);

				BINARY_ARITHMETIC_OP(ILOP_iadd4, Int32, +);
				BINARY_ARITHMETIC_OP(ILOP_isub4, Int32, -);
				BINARY_ARITHMETIC_OP(ILOP_imul4, Int32, *);
				BINARY_ARITHMETIC_OP_ZERO_CHECK(ILOP_idiv4, Int32, /);
				BINARY_ARITHMETIC_OP_ZERO_CHECK(ILOP_imod4, Int32, %);
				UNARY_ARITHMETIC_OP(ILOP_ineg4, Int32, -);

				NUMERIC_COMPARE_OP(ILOP_ige4, Int32, >=);
				NUMERIC_COMPARE_OP(ILOP_igt4, Int32, >);
				NUMERIC_COMPARE_OP(ILOP_ile4, Int32, <=);
				NUMERIC_COMPARE_OP(ILOP_ilt4, Int32, <);
				NUMERIC_COMPARE_OP(ILOP_ine4, Int32, !=);
				NUMERIC_COMPARE_OP(ILOP_ieq4, Int32, ==);
				
				BINARY_ARITHMETIC_OP(ILOP_iadd8, Int64, +);
				BINARY_ARITHMETIC_OP(ILOP_isub8, Int64, -);
				BINARY_ARITHMETIC_OP(ILOP_imul8, Int64, *);
				BINARY_ARITHMETIC_OP_ZERO_CHECK(ILOP_idiv8, Int64, /);
				BINARY_ARITHMETIC_OP_ZERO_CHECK(ILOP_imod8, Int64, %);
				UNARY_ARITHMETIC_OP(ILOP_ineg8, Int64, -);

				NUMERIC_COMPARE_OP(ILOP_ige8, Int64, >=);
				NUMERIC_COMPARE_OP(ILOP_igt8, Int64, >);
				NUMERIC_COMPARE_OP(ILOP_ile8, Int64, <=);
				NUMERIC_COMPARE_OP(ILOP_ilt8, Int64, <);
				NUMERIC_COMPARE_OP(ILOP_ine8, Int64, !=);
				NUMERIC_COMPARE_OP(ILOP_ieq8, Int64, ==);

				MOVE_OP_GROUP(ILOP_move1, UnitCopier, Int8);
				MOVE_OP_GROUP(ILOP_move2, UnitCopier, Int16);
				MOVE_OP_GROUP(ILOP_move4, UnitCopier, Int32);
				MOVE_OP_GROUP(ILOP_move8, UnitCopier, Int64);
				MOVE_OP_GROUP(ILOP_movertp, UnitCopier, RuntimePointer<void>);
				MOVE_OP_GROUP(ILOP_movesz, BlockCopier, UInt8);

				CONVERT_OP(ILOP_isx18, Int8, Int64);
				CONVERT_OP(ILOP_isx28, Int16, Int64);
				CONVERT_OP(ILOP_isx48, Int32, Int64);
				CONVERT_OP(ILOP_isx14, Int8, Int32);
				CONVERT_OP(ILOP_isx24, Int16, Int32);
				CONVERT_OP(ILOP_isx12, Int8, Int16);

				CONVERT_OP(ILOP_ict84, Int64, Int32);
				CONVERT_OP(ILOP_ict82, Int64, Int16);
				CONVERT_OP(ILOP_ict81, Int64, Int8);
				CONVERT_OP(ILOP_ict42, Int32, Int16);
				CONVERT_OP(ILOP_ict41, Int32, Int8);
				CONVERT_OP(ILOP_ict21, Int16, Int8);

				CONVERT_OP(ILOP_nconv8, Int64, Int64);
				CONVERT_OP(ILOP_nconv4, Int32, Int32);
				CONVERT_OP(ILOP_nconv2, Int16, Int16);
				CONVERT_OP(ILOP_nconv1, Int8, Int8);

				CONVERT_OP(ILOP_itof44, Int32, Float32);
				CONVERT_OP(ILOP_itof48, Int32, Float64);
				CONVERT_OP(ILOP_itof84, Int64, Float32);
				CONVERT_OP(ILOP_itof88, Int64, Float64);
			
				CONVERT_OP(ILOP_ftoi44, Float32, Int32);
				CONVERT_OP(ILOP_ftoi48, Float32, Int64);
				CONVERT_OP(ILOP_ftoi84, Float64, Int32);
				CONVERT_OP(ILOP_ftoi88, Float64, Int64);

				BRANCH_OP(ILOP_jeq_f1, UInt8, false, true);
				BRANCH_OP(ILOP_jeq_f2, UInt16, false, true);
				BRANCH_OP(ILOP_jeq_f4, UInt32, false, true);
				BRANCH_OP(ILOP_jeq_f8, UInt64, false, true);

				BRANCH_OP(ILOP_jeq_p1, UInt8, true, true);
				BRANCH_OP(ILOP_jeq_p2, UInt16, true, true);
				BRANCH_OP(ILOP_jeq_p4, UInt32, true, true);
				BRANCH_OP(ILOP_jeq_p8, UInt64, true, true);

				BRANCH_OP(ILOP_jne_f1, UInt8, false, false);
				BRANCH_OP(ILOP_jne_f2, UInt16, false, false);
				BRANCH_OP(ILOP_jne_f4, UInt32, false, false);
				BRANCH_OP(ILOP_jne_f8, UInt64, false, false);

				BRANCH_OP(ILOP_jne_p1, UInt8, true, false);
				BRANCH_OP(ILOP_jne_p2, UInt16, true, false);
				BRANCH_OP(ILOP_jne_p4, UInt32, true, false);
				BRANCH_OP(ILOP_jne_p8, UInt64, true, false);

				default:
					{
						THROWEXCEPTION(objm->GetBuiltIns()->providerDictionary[X_InvalidOperationException]);
					}
					break;
				};
			}	// Main loop
		}		// Func

		int RDX_DECL_API ResumeThread(OperationContext *ctx, IObjectManager *objm, RuntimeThread *thread)
		{
			int status = RuntimeState::Active;

			// Cycle through code until the status turns into an exit code
			while(status > 0)
			{
				const Method *method = thread->frame.method;

				if(method == NULL)
					return RuntimeState::Exited;

				if(method->_native.precompiledCodeModule)
					status = PrecompiledCodeModule::ResumeThread(ctx, objm, thread);
				else
					status = ResumeThreadInterpreted(ctx, objm, thread);

				if(status == RuntimeState::AbandonFrame)
				{
					// Code signalled to abandon the frame, determine the type of frame being entered
					RuntimeStackFrame *rsf = thread->frame.bp;
					if(rsf->method)
					{
						// Standard frame
						thread->frame = *rsf;
						status = RuntimeState::Active;
					}
					else
					{
						// Root-level call frame
						RuntimeStackFrameRoot *rframe = static_cast<RuntimeStackFrameReentrant *>(rsf);
						thread->insertionPoint = rframe->insertionPoint;
						if(rframe->aboveNative)
						{
							// Reentrant frame
							RuntimeStackFrameReentrant *ref = static_cast<RuntimeStackFrameReentrant *>(rframe);
							thread->activeNativeMethod = ref->nativeMethod;
							thread->frame = ref->subNativeFrame;
							return RuntimeState::Exited;
						}
						else
						{
							// Absolute root frame
							thread->activeNativeMethod = NULL;
							thread->Reset();
							return RuntimeState::Exited;
						}
					}
				}
			}

			return status;
		}

		int InterpreterCodeProvider::RunMethod(OperationContext *ctx, IObjectManager *objm, const Method *m, RuntimeThread *thread, RuntimeStackValue *prv) const
		{
			const Method *viaMethod = thread->activeNativeMethod;
			const GCInfo *methodGCI = GCInfo::From(m);
			int status;

			if(m->_native.isNativeCall)
			{
				thread->activeNativeMethod = m;
				status = m->_native.nativeCall(ctx, objm, m, thread, prv);
			}
			else
			{
				const void *stackBottom = thread->stackBytes + thread->stackCapacity;
				if(!EnterMethodRoot(objm, m, &thread->precallFrame, thread->precallFrame.bp, thread->stackBytes, prv, &thread->frame, thread->insertionPoint, (thread->insertionPoint != stackBottom), viaMethod, &thread->frame))
				{
					Exception *stackOverflowException = static_cast<Exception *>(objm->GetBuiltIns()->providerDictionary[X_StackOverflowException]);
					thread->ex = stackOverflowException;
					return RuntimeState::Exception;
				}

				thread->activeNativeMethod = NULL;
				status = ResumeThread(ctx, objm, thread);
			}
			return status;
		}

		LargeInt RDX_DECL_API IPToCurrentInstruction(const Method *method, const void *ip)
		{
			IObjectManager *objm = GCInfo::From(method)->ownerObjectManager;

			if(method->_native.precompiledCodeModule)
				return PrecompiledCodeModule::IPToCurrentInstruction(method, ip);

			return static_cast<const ILInstruction*>(ip) - static_cast<const ILInstruction*>(method->_native.nativeInstructions) - 1;
		}

		const void *RDX_DECL_API InstrNumToIP(OperationContext *ctx, const Method *method, LargeInt instrNum, bool *resumeAllowed)
		{
			if(method->_native.precompiledCodeModule)
				return PrecompiledCodeModule::InstrNumToIP(ctx, method, instrNum, resumeAllowed);

			const ILInstruction *ili = static_cast<const ILInstruction*>(method->_native.nativeInstructions) + instrNum;
			if(resumeAllowed)
			{
				bool flag = ((method->_native.ilResumeFlags[instrNum/8] & (1 << (instrNum & 0x7))) != 0);
				*resumeAllowed = flag;
			}
			return ili;
		}


		void InterpreterCodeProvider::CreateExecutable(IObjectManager *objm, OperationContext *ctx, Method *m) const
		{
			RDX_TRY(ctx)
			{
				for(LargeInt i=0;i<_numModules;i++)
				{
					if(_modules[i] == NULL)
						continue;

					const void *ip = _modules[i]->IPForMethod(m);
					if(ip != NULL)
					{
						m->_native.nativeInstructions = const_cast<void*>(ip);
						m->_native.precompiledCodeModule = _modules[i];

						const String *str = GCInfo::From(m)->gstSymbol;
						if(str != NULL)
						{
							const PrecompiledFunctionInfo *funcInfo = _modules[i]->GetFunctionInfo(str);
							if(funcInfo != NULL)
							{
								// If this is precompiled, we can discard the arguments as well
								m->_native.compactArgs = NULL;
								m->_native.largeArgs = NULL;
							}
						}

						m->_native.ilinstructions = NULL;
						return;
					}
				}

				// Convert opcodes to faster ones
				const ILInstruction *originalInstructions = m->_native.ilinstructions;
				const Char *methodName = GCInfo::From(m)->gstSymbol->AsChars();
				LargeInt numInstructions = GCInfo::From(originalInstructions)->numElements;

				ILInstruction *optimizedInstr = objm->GetAllocator()->CAlloc<ILInstruction>(numInstructions);
				if(!optimizedInstr)
					RDX_STHROW(ctx, Errors::RDX_ERROR_ALLOCATION_FAILED);

				m->_native.nativeInstructions = optimizedInstr;

				for(LargeInt i=0;i<numInstructions;i++)
				{
					ILInstruction *instr = optimizedInstr + i;
					*instr = originalInstructions[i];

					if(instr->opcode >= ILOP_intrinsic)
					{
						bool matched = false;
						LargeInt numSingle = sizeof(SINGLE_ARG_INTRINSICS) / sizeof(SINGLE_ARG_INTRINSICS[0]);
						LargeInt numDual = sizeof(DUAL_ARG_INTRINSICS) / sizeof(DUAL_ARG_INTRINSICS[0]);

						// Convert to an optimal op
						for(LargeInt intri=0;intri<numSingle;intri++)
						{
							const SingleArgIntrinsic *intr = SINGLE_ARG_INTRINSICS + intri;
							if(instr->opcode == intr->originalOp && instr->arg.ca[2].li == intr->p2check)
							{
								instr->opcode = intr->newOp;
								matched = true;
								break;
							}
						}

						for(LargeInt intri=0;intri<numDual;intri++)
						{
							const DualArgIntrinsic *intr = DUAL_ARG_INTRINSICS + intri;
							if(instr->opcode == intr->originalOp &&
								instr->arg.ca[2].li == intr->p2check &&
								instr->arg.ca[3].li == intr->p3check)
							{
								instr->opcode = intr->newOp;
								matched = true;
								break;
							}
						}
					}
					else if(instr->opcode == ILOP_iteratearray)
					{
						if(instr->arg.ca[4].li == 8 && (instr->arg.ca[5].li & 7) == 0)
							instr->opcode = ILOP_iteratearray8;
						else if(instr->arg.ca[4].li == 4 && (instr->arg.ca[5].li & 3) == 0)
							instr->opcode = ILOP_iteratearray4;
					}
					else if(instr->opcode == ILOP_iteratearraysub)
					{
						if(instr->arg.ca[4].li == 8 && (instr->arg.ca[5].li & 7) == 0)
							instr->opcode = ILOP_iteratearraysub8;
						else if(instr->arg.ca[4].li == 4 && (instr->arg.ca[5].li & 3) == 0)
							instr->opcode = ILOP_iteratearraysub4;
					}
					else if(instr->opcode == ILOP_move)
					{
						LargeInt translatedOp = ILOP_unused;
						if(instr->arg.ca[2].li == 8 && (instr->arg.ca[3].li & 7) == 0)
							translatedOp = ILOP_move8;
						else if(instr->arg.ca[2].li == sizeof(RuntimePointer<void>) && (instr->arg.ca[3].li & (RDX_ALIGNOF(RuntimePointer<void>) - 1)) == 0)
							translatedOp = ILOP_movertp;
						else if(instr->arg.ca[2].li == 4 && (instr->arg.ca[3].li & 3) == 0)
							translatedOp = ILOP_move4;
						else if(instr->arg.ca[2].li == 2 && (instr->arg.ca[3].li & 1) == 0)
							translatedOp = ILOP_move2;
						else if(instr->arg.ca[2].li == 1)
							translatedOp = ILOP_move1;
						else
							translatedOp = ILOP_movesz;

						if(translatedOp != ILOP_unused)
						{
							// Determine offsetting
							if(instr->arg.ca[4].li & ILOpcodes::ILOP_MOVE_SRC_PARENT_FRAME)
								translatedOp += MOVE_SRC_MODE_PARENT;
							else if(instr->arg.ca[4].li & ILOpcodes::ILOP_MOVE_SRC_TYPE_DEFAULT)
								translatedOp += MOVE_SRC_MODE_ABSOLUTE;

							if(instr->arg.ca[4].li & ILOpcodes::ILOP_MOVE_SRC_DEREF)
								translatedOp += MOVE_SRC_DEREF_DEREF * (MOVE_SRC_MODE_COUNT);

							if(instr->arg.ca[4].li & ILOpcodes::ILOP_MOVE_DEST_PARENT_FRAME)
								translatedOp += MOVE_DEST_MODE_PARENT * (MOVE_SRC_MODE_COUNT * MOVE_SRC_DEREF_COUNT);

							if(instr->arg.ca[4].li & ILOpcodes::ILOP_MOVE_DEST_DEREF)
								translatedOp += MOVE_SRC_DEREF_DEREF * (MOVE_SRC_MODE_COUNT * MOVE_SRC_DEREF_COUNT * MOVE_DEST_MODE_COUNT);

							instr->opcode = static_cast<ILOpcode>(translatedOp);
						}
					}
					else if(instr->opcode == ILOP_immediate)
					{
						switch(instr->arg.la[1].ca[0].lui)
						{
						case 1: instr->opcode = ILOP_immediate1; break;
						case 2: instr->opcode = ILOP_immediate2; break;
						case 4: instr->opcode = ILOP_immediate4; break;
						case 8: instr->opcode = ILOP_immediate8; break;
						}
					}
					else if(instr->opcode == ILOP_zero)
					{
						switch(instr->arg.ca[1].lui)
						{
						case 1: instr->opcode = ILOP_zero1; break;
						case 2: instr->opcode = ILOP_zero2; break;
						case 4: instr->opcode = ILOP_zero4; break;
						case 8: instr->opcode = ILOP_zero8; break;
						}
					}
					else if(instr->opcode == ILOP_jeq_f || instr->opcode == ILOP_jeq_p || instr->opcode == ILOP_jne_f || instr->opcode == ILOP_jne_p)
					{
						int translation = -1;
						if(instr->arg.ca[2].li == 8 && (instr->arg.ca[3].li & 7) == 0)
							translation = 3;
						else if(instr->arg.ca[2].li == 4 && (instr->arg.ca[3].li & 3) == 0)
							translation = 2;
						else if(instr->arg.ca[2].li == 2 && (instr->arg.ca[3].li & 1) == 0)
							translation = 1;
						else if(instr->arg.ca[2].li == 1)
							translation = 0;

						if(translation != -1)
						{
							switch(instr->opcode)
							{
							case ILOP_jeq_f: instr->opcode = static_cast<ILOpcode>(ILOP_jeq_f1 + translation); break;
							case ILOP_jeq_p: instr->opcode = static_cast<ILOpcode>(ILOP_jeq_p1 + translation); break;
							case ILOP_jne_f: instr->opcode = static_cast<ILOpcode>(ILOP_jne_f1 + translation); break;
							case ILOP_jne_p: instr->opcode = static_cast<ILOpcode>(ILOP_jne_p1 + translation); break;
							}
						}
					}
				}
				// Done

				// Throw out the old instruction data
				m->_native.ilinstructions = 0;
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		void InterpreterCodeProvider::ReleaseExecutable(IObjectManager *objm, Method *m) const
		{
			if(!m->_native.precompiledCodeModule)
			{
				objm->GetAllocator()->Free(m->_native.nativeInstructions);
				m->_native.nativeInstructions = NULL;
			}
			// Interpreter uses the IL instructions list, which is garbage collected
		}

		void InterpreterCodeProvider::InitializeSymbolDictionary(OperationContext *ctx, IObjectManager *objm) const
		{
			RDX_TRY(ctx)
			{
				void **dict = objm->GetBuiltIns()->providerDictionary.Cast<void*>();

				if(!dict)
				{
					CRef<const String> str;
						
					objm->GetBuiltIns()->providerDictionaryTracedSymbolCount = CPD_NumDictionaryTracedObjects;

					RDX_PROTECT_ASSIGN(ctx, objm->GetBuiltIns()->providerDictionary, objm->Create1DArray<Exception*>(ctx, CPD_NumDictionaryValues).ReinterpretCast<void*>() );
					objm->GetBuiltIns()->providerDictionaryTracedSymbolCount = CPD_NumDictionaryTracedObjects;
					dict = objm->GetBuiltIns()->providerDictionary;

					RDX_PROTECT_ASSIGN(ctx, str, objm->CreateStringASCII(ctx, "Core.RDX.NullReferenceException.instance") );
					RDX_PROTECT_ASSIGN(ctx, dict[X_NullReferenceException], objm->LookupSymbolSimple(ctx, str).Cast<Exception>() );

					RDX_PROTECT_ASSIGN(ctx, str, objm->CreateStringASCII(ctx, "Core.RDX.IndexOutOfBoundsException.instance") );
					RDX_PROTECT_ASSIGN(ctx, dict[X_IndexOutOfBoundsException], objm->LookupSymbolSimple(ctx, str).Cast<Exception>() );

					RDX_PROTECT_ASSIGN(ctx, str, objm->CreateStringASCII(ctx, "Core.RDX.AllocationFailureException.instance") );
					RDX_PROTECT_ASSIGN(ctx, dict[X_AllocationFailureException], objm->LookupSymbolSimple(ctx, str).Cast<Exception>() );

					RDX_PROTECT_ASSIGN(ctx, str, objm->CreateStringASCII(ctx, "Core.RDX.IncompatibleConversionException.instance") );
					RDX_PROTECT_ASSIGN(ctx, dict[X_IncompatibleConversionException], objm->LookupSymbolSimple(ctx, str).Cast<Exception>() );

					RDX_PROTECT_ASSIGN(ctx, str, objm->CreateStringASCII(ctx, "Core.RDX.InvalidOperationException.instance") );
					RDX_PROTECT_ASSIGN(ctx, dict[X_InvalidOperationException], objm->LookupSymbolSimple(ctx, str).Cast<Exception>() );

					RDX_PROTECT_ASSIGN(ctx, str, objm->CreateStringASCII(ctx, "Core.RDX.UnspecifiedException.instance") );
					RDX_PROTECT_ASSIGN(ctx, dict[X_UnspecifiedException], objm->LookupSymbolSimple(ctx, str).Cast<Exception>() );

					RDX_PROTECT_ASSIGN(ctx, str, objm->CreateStringASCII(ctx, "Core.RDX.StackOverflowException.instance") );
					RDX_PROTECT_ASSIGN(ctx, dict[X_StackOverflowException], objm->LookupSymbolSimple(ctx, str).Cast<Exception>() );

					RDX_PROTECT_ASSIGN(ctx, str, objm->CreateStringASCII(ctx, "Core.RDX.DivideByZeroException.instance") );
					RDX_PROTECT_ASSIGN(ctx, dict[X_DivideByZeroException], objm->LookupSymbolSimple(ctx, str).Cast<Exception>() );

					dict[CPD_CodeProvider] = const_cast<void *>(static_cast<const void *>(this));
				}
			}
			RDX_CATCH(ctx)
			{
				RDX_RETHROW(ctx);
			}
			RDX_ENDTRY
		}

		//NativeCallback InterpreterCodeProvider::GetNativeCallback() const
		//{
		//	return RunMethod;
		//}

		IPToCurrentInstructionCallback InterpreterCodeProvider::GetIPToCurrentInstructionCallback() const
		{
			return IPToCurrentInstruction;
		}

		InstrNumToIPCallback InterpreterCodeProvider::GetInstrNumToIPCallback() const
		{
			return InstrNumToIP;
		}

		ResumeThreadCallback InterpreterCodeProvider::GetResumeThreadCallback(IObjectManager *objm) const
		{
			return ResumeThread;
		}

		void InterpreterCodeProvider::Shutdown()
		{
			_alloc.Free(this);
		}
	}
}
